package cn.huaqingcheng.tianshu.core.auth.session;

import cn.huaqingcheng.tianshu.core.auth.jwt.JwtPayload;
import cn.huaqingcheng.tianshu.core.auth.jwt.JwtService;
import cn.huaqingcheng.tianshu.core.uc.model.Account;
import cn.huaqingcheng.tianshu.core.uc.service.AccountService;
import cn.huaqingcheng.tianshu.security.token.JwtTokenAuthentication;
import cn.huaqingcheng.tianshu.security.token.JwtTokenParsing;
import com.auth0.jwt.exceptions.TokenExpiredException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import jakarta.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static org.springframework.web.context.request.RequestAttributes.SCOPE_REQUEST;

/**
 * JwtTokenParsingImpl
 */
@Component
@RequiredArgsConstructor
public class JwtTokenParsingImpl implements JwtTokenParsing {

    private static final String NAME = "%s_PARSING_JWT_TOKEN_AUTHENTICATION".formatted(JwtTokenParsingImpl.class.getName());

    /**
     * OAuth 2.0 令牌
     *
     * @see <a href="https://datatracker.ietf.org/doc/html/rfc6750">RFC 6750</a>
     * @see <a href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Guides/Authentication#%E8%BA%AB%E4%BB%BD%E9%AA%8C%E8%AF%81%E6%96%B9%E6%A1%88">身份验证方案</a>
     */
    private static final Pattern AUTH_SCHEME_PATTERN = Pattern.compile("Bearer\\s+(?<token>\\S+)", Pattern.CASE_INSENSITIVE);

    private final JwtService jwtService;

    private final AccountService accountService;

    @Override
    public JwtTokenAuthentication parsing(String authorizationValue, HttpServletRequest request) {
        RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
        Objects.requireNonNull(attributes);
        Object attribute = attributes.getAttribute(NAME, SCOPE_REQUEST);

        if (attribute instanceof JwtTokenAuthentication jwtTokenAuthentication) {
            return jwtTokenAuthentication;
        }

        if (authorizationValue == null || authorizationValue.isEmpty()) {
            return null;
        }
        Matcher matcher = AUTH_SCHEME_PATTERN.matcher(authorizationValue);
        if (!matcher.matches()) {
            return null;
        }
        String token = matcher.group("token");

        JwtPayload payload;
        try {
            payload = jwtService.verifyRead(token);
        } catch (TokenExpiredException e) {
            return null;
        }

        Account account = accountService.getById(payload.getAccountId())
                .orElseThrow();

        JwtTokenAuthentication authentication = new JwtTokenAuthentication();
        authentication.setName(account.getUsername());
        authentication.setCredentials(token);
        authentication.setDetails(payload);
        authentication.setPrincipal(account);
        authentication.setAuthenticated(true);
        authentication.setAuthorities(List.of());

        attributes.setAttribute(NAME, authentication, SCOPE_REQUEST);

        return authentication;
    }

}
